package com.demo.webapp.ui.table.taglib;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.demo.core.dao.base.support.PageSupport;
import com.demo.core.service.exception.PageSupportNotFoundException;
import com.demo.webapp.ui.DisplayerConfig;
import com.demo.webapp.ui.access.DisplayerAuthority;
import com.demo.webapp.ui.access.PermissionsAdapter;
import com.demo.webapp.ui.access.impl.ExpressionEvaluationPermissionsAdapter;
import com.demo.webapp.ui.access.impl.RolesPermissionsAdapter;
import com.demo.webapp.ui.displayer.Displayer;
import com.demo.webapp.ui.displayer.DisplayerMapping;
import com.demo.webapp.ui.displayer.MessageResourcesDisplayer;
import com.demo.webapp.ui.table.ColumnComponent;
import com.demo.webapp.ui.table.TableComponent;
import com.demo.webapp.ui.table.TableRepositoryResource;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;


/**
 * Table的Jsp标签
 * 使用标签 example for：
 * <pre>
 *  &lt;demo:table name="ListMenu"&gt;
 *     &lt;demo:column name="MyTable"/&gt;
 *  &lt;/demo:table&gt;
 * </pre>
 * 
 * @author ryuu.kk
 * @version 1.16 @at 2012/04/28 
 *
 */
public class TableDisplayerTag extends TagSupport {

	/**
	 * 版本序列化ID
	 */
	private static final long serialVersionUID = -5883180388888463228L;

	/** 日志 **/
	private static Log log = LogFactory.getLog(TableDisplayerTag.class);
	
	/** 持久Table组件的特征Key **/
	public static final String PRIVATE_REPOSITORY = "com.demo.webapp.ui.table.TableRepositoryKey";
	
    /** 向ColumnTag传递展示器的特征Key **/
    public static final String DISPLAYER_KEY = "com.demo.webapp.ui.table.DISPLAYER";
    
    /** TABLE组件的特征Key**/
    public static final String TABLE_COMPONENT_KEY = "com.demo.webapp.ui.table.TABLE";
    
    /** 访问授权适配器,默认为角色 **/
    public static final String ROLES_ADAPTER = "rolesAdapter";
    /** table的默认Key **/
    public static final String Table_ID = "tableId";
    /** Lib的默认资源包 **/
    protected static ResourceBundle messages = ResourceBundle.getBundle("com.demo.webapp.ui.table.taglib.LocalStrings");

    //~ Instance fields ========================================================
    
    /** Table的ID**/
    private String id;
    
    /** Table的配置名 **/
    private String name;
    
    /** Table的属性名,使用属性名,取得对应的内容 **/
    private String property;
    
    /** Table的分页大小;如果为0或-1代表不分页 **/
    private int pagesize = -1;
    /** Table的分页地址;如果不指定,则使用当前地址**/
    private String page;
    /** Table的分页是否采用ajax分页;默认不使用 **/
    private String ajaxPage = "false";
    /** 
     * Table的ajax分页后的渲染点
     * 当ajax分页后,得到的页面需要渲染到某个DOM节点
     **/
    private String renderTo;
    
    /**
     * 页面渲染完成后的js调用
     */
    private String onLoad;
    
    /** Table的column是否分页**/
    private String sort = "false";
    
    private String bundleKey;
    /** 标签自定义的本地化Key; 默认使用其他策略 **/
    private String localeKey;
    private String permissions;
    private String repository;
    private String config = Displayer.DEFAULT_CONFIG;
    
    /**
     * Table组件.
     * 当持久化容器中无法找到该组件时,根据配置创建一个Table组件
     */
    private Displayer<TableComponent> tableDisplayer;
    
    /**
     * 多语言资源包
     */
    private ResourceBundle rb;// used to allow setting of ResourceBundle 
    							// from JSTL in EL tag
    //使用Spring's MessageSource for locale message;
    private MessageSource messageSource;
    //Spring容器
    private ApplicationContext applicationContext; 
    //~ Methods ================================================================

	public void setConfig(String config) {
        if (log.isDebugEnabled()) {
            log.debug("setting config to: " + config);
        }
        this.config = config;
    }
	
    public String getConfig() {
        return config;
    }

	public void setBundle(String bundleKey) {
		this.bundleKey = bundleKey;
	}

    public String getBundle() {
		return bundleKey;
	}

    public void setLocale(String locale) {
        this.localeKey = locale;
    }
    
    public String getLocale() {
        return localeKey;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public String getProperty() {
		return property;
	}

	public void setProperty(String property) {
		this.property = property;
	}

	public void setId(String id) {
        this.id = id;
    }

    public String getRepository() {
        return repository;
    }

    public void setPage(String page) {
		this.page = page;
	}

	public void setPagesize(int pagesize) {
		this.pagesize = pagesize;
	}

	/**
     * 这个方法设置Menu持久资源名称
     * 默认的使用{@link MenuDisplayerTag.MENU_REPOSITORY_KEY}
     * @param repository 持久资源名
     */
    public void setRepository(String repository) {
        this.repository = repository;
    }

    public void setRenderTo(String renderTo) {
		this.renderTo = renderTo;
	}
    
    public void setOnLoad(String onload) {
    	this.onLoad = onload;
    }

	public void setSort(String sort) {
		this.sort = sort;
	}

	public void setAjaxPage(String ajaxPage) {
		this.ajaxPage = ajaxPage;
	}

	/** 
     * 取得许可对象名称
     * @return 许可对象名称
     */
    public String getPermissions() {
        return this.permissions;
    }

    /** 
     * 设置许可对象名称
     * 许可对象可以是注册在IoC容器的名称
     * 默认使用:{@link 角色许可}
     * @param 许可字段
     */
    public void setPermissions(String permissions) {
        this.permissions = permissions;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
	public int doStartTag() throws JspException {
        if (repository == null) {
            repository = TableRepositoryResource.TABLE_REPOSITORY_KEY;
        }

        if (log.isDebugEnabled()) {
            log.debug("Looking for repository named '" + repository + "'");
        }

        //取得Table的数据持久化仓库
        TableRepositoryResource rep = (TableRepositoryResource) pageContext.findAttribute(this.repository);

        if (rep == null) {
            throw new JspException(messages.getString("menurepository.not.found"));
        } else {
        	//设置该持久仓库,将其放入pageContext中,使内层控件MenuTag可以独占使用该容器仓库。
            if (log.isDebugEnabled()) {
                log.debug("stuffing repository into pageContext...");
            }
            pageContext.setAttribute(PRIVATE_REPOSITORY, rep);
        }

        // 取得显示器Map对象
        DisplayerMapping displayerMapping = rep.getMapping();

        if (displayerMapping == null) {
            throw new JspException(messages.getString("displayer.mapping.not.found"));
        }
        //Displayer displayerInstance = displayerMapping.get(this.name);
        Displayer displayerInstance = null;
        if (displayerInstance == null) {
            //创建新MenuDisplayer实例
            try {
            	DisplayerConfig disConfig = null;
            	List<DisplayerConfig> configList = displayerMapping.getDisplayerConfig();
            	if (configList == null || configList.size() == 0) {
            		throw new Exception(messages.getString("menurepository.displayer.conifg.not.fount"));
            	}
            	for (DisplayerConfig config : configList) {
            		if (config.getName().equals(this.name)) {
            			disConfig = config;
            			break;
            		}
            	}
            	//不存在指定的配置信息
            	if (disConfig == null) {
            		throw new Exception(messages.getString("menurepository.displayer.conifg.not.fount"));
            	}
            	//创建一个Displayer的新实例对象
                displayerInstance = (Displayer) Class.forName(disConfig.getType()).newInstance();
                /* 
                 * fix:Bug in Here,必须是单例模式,否则抛出jsp异常,待查原因
                 * if (!displayerMapping.containsKey(this.name)) {
                 *	displayerInstance.setConfig(disConfig.getConfig());
                 *	displayerInstance.setName(disConfig.getName());
                 *	displayerMapping.put(displayerInstance);
                 * }
                 */
                tableDisplayer = displayerInstance;
            } catch (Exception e) {
                throw new JspException(e.getMessage());
            }
        }
        //多语言资源Key
        if (bundleKey == null || "".equals(bundleKey)) {
            this.bundleKey = "org.apache.struts.action.MESSAGE";
        	//this.bundleKey = "com.demo.webapp.ui.MESSAGE";
        }
        
        // setup the displayerInstance
        // if the displayer is a MessageResourceMenuDisplayer
        // and a bundle is specified, then pass it the bundle (message resources) and
        // the locale
        if (displayerInstance instanceof MessageResourcesDisplayer) {
        	MessageResourcesDisplayer mrDisplayerInstance = (MessageResourcesDisplayer) displayerInstance;
        	
            Locale locale;
            if (localeKey == null) {
                // 默认使用Spring的Locale
            	locale = LocaleContextHolder.getLocale();
            	if (locale == null) {
                	//使用struts的Locale
            		locale = (Locale) pageContext.findAttribute("org.apache.struts.action.LOCALE");
            	}
                if (locale == null) {
                	//使用浏览器的Locale
                    locale = pageContext.getRequest().getLocale();
                }
            } else {
                locale = (Locale) pageContext.findAttribute(localeKey);
            }
            //mrDisplayerInstance.setLocale(locale);
            
            if (messageSource != null) {
                mrDisplayerInstance.setMessageBundle(messageSource);
            } else {
                //使用bundleKey取得Bundle：
                //	①、Spring上下文取得;
                //	②、Struts2的对语言Bundle；
                //	③、字符串传入,Displayer自行处理
            	if (applicationContext == null) {
            		applicationContext = WebApplicationContextUtils.getWebApplicationContext(pageContext.getServletContext());
            	}
                if (applicationContext != null && applicationContext.containsBean(bundleKey)) {
                	try {
						messageSource = applicationContext.getBean(bundleKey, MessageSource.class);
						mrDisplayerInstance.setMessageBundle(messageSource);
					} catch (BeansException e) {
						log.error(e.getMessage());
					}
                } else {
                	//如果为空则无法取得Spring上下文、不是Spring项目
                	Object resources = pageContext.findAttribute(bundleKey);
	                if (resources == null) {
	                    //取得ResourceBundle
	                    try {
	                        rb = ResourceBundle.getBundle(bundleKey, locale);
	                        mrDisplayerInstance.setMessageBundle(rb);
	                    } catch (MissingResourceException mre) {
	                        log.error(mre.getMessage());
	                    }
	                } else {
	                     mrDisplayerInstance.setMessageBundle(resources);   
	                }
                }
            }
        }
        //将用户配置传递到Displayer中
        displayerInstance.setConfig(config);
        if (id != null) {
            pageContext.setAttribute(Table_ID, id);
        }

        displayerInstance.init(pageContext, displayerMapping);
        if (displayerInstance instanceof DisplayerAuthority<?>) {
        	((DisplayerAuthority) displayerInstance).setPermissionsAdapter(getPermissionsAdapter());
        }
        //将展示层对象放入pageContext上下文中,使内层控件MenuTag可以使用该对象对Menu进行渲染。	
        pageContext.setAttribute(DISPLAYER_KEY, displayerInstance);
        
        TableComponent component = null;
        try {
        	component = tableBuilder();
		} catch (PageSupportNotFoundException e) {
			try {
				pageContext.getOut().println(e.getMessage());
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
        
        //this.property;
        Object source = sourceBuilder();
        component.setSource(source);
        
        pageContext.setAttribute(TABLE_COMPONENT_KEY, component);
        
        return (EVAL_BODY_INCLUDE);
    }

    @SuppressWarnings("rawtypes")
	protected PermissionsAdapter<?> getPermissionsAdapter()
            throws JspException {
        PermissionsAdapter<?> adapter = null;

        if (permissions != null) {
            //如果设置许可名称为 "rolesAdapter",则自动创建RolesPermissionsAdapter对象
            if (permissions.equalsIgnoreCase(ROLES_ADAPTER)) {
                adapter = new RolesPermissionsAdapter((HttpServletRequest) pageContext.getRequest());
            } else {
            	//搜索Spring容器的bean
            	if (applicationContext != null ){
            		if (applicationContext.containsBean(permissions)) {
            			adapter = applicationContext.getBean(permissions, PermissionsAdapter.class);
            		} else {
            			//基于表达式的授权方式
            			adapter = new ExpressionEvaluationPermissionsAdapter(permissions, (HttpServletRequest) pageContext.getRequest());
            		}
            	} else {
            		 adapter = (PermissionsAdapter) pageContext.findAttribute(permissions);
            	}
               
                if (adapter == null) {
                    throw new JspException(messages.getString("permissions.not.found"));
                }
            }
        }

        return adapter;
    }

    public int doEndTag() throws JspException {
    	
    	TableComponent component = (TableComponent) pageContext.getAttribute(TABLE_COMPONENT_KEY);
    	
    	try {
			tableDisplayer.display(component);
		} catch (IOException e) {
			try {
				pageContext.getOut().println(e.getMessage());
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
    	
        tableDisplayer.end(pageContext);
        pageContext.removeAttribute(DISPLAYER_KEY);
        pageContext.removeAttribute(PRIVATE_REPOSITORY);
        pageContext.removeAttribute(TABLE_COMPONENT_KEY);
        return (EVAL_PAGE);
    }

    public void release() {
        if (log.isDebugEnabled()) {
            log.debug("release() called");
        }

        this.bundleKey = null;
        this.config = Displayer.DEFAULT_CONFIG;
        this.localeKey = null;
        this.name = null;
        //this.menuDisplayer = null;
        this.repository = null;
        this.permissions = null;
        this.property = null;
        this.rb = null;
    }
    
    protected TableComponent tableBuilder() throws PageSupportNotFoundException {
    	
    	TableComponent component = new TableComponent();
    	component.setId(id);
    	component.setPage(false);
    	component.setSort("true".equals(this.sort));
    	// 瞬时对象:目前未使用
    	component.setStatus(TableComponent.COMPONENT_INSTANT);
    	component.setPerperty(this.property);
    	
//    	if (StringUtils.isNotBlank(this.property)) {
//    		component.set
//    	}
    	// Column
    	List<ColumnComponent> columnList = new ArrayList<ColumnComponent>();
    	component.setColumnList(columnList);
    	// 页面渲染完成后的js调用
    	component.setOnLoad(this.onLoad);
    	if (pagesize > 0) {
    		component.setPage(true);
    		PageSupport pageSupport = getPageSupport();
    		component.setPageSupport(pageSupport);
    		component.setPagesize(pagesize);
    		component.setPage(this.page);
    		component.setAjaxPage("true".equals(this.ajaxPage));
    		component.setRenderTo(renderTo);
    	}
		return component;
    }

    protected Object sourceBuilder() {
    	
    	ActionContext context = ActionContext.getContext();
		ValueStack vs = context.getValueStack();
		Object target  = vs.findValue(this.property, true);
		
		return target;
    }
	/**
	 * 这是Struts2的PageSupport取得方式,可扩展为其他方式取得
	 * 
	 * @return PageSupport 分页支持
	 * @throws PageSupportNotFoundException 未找到PageSupport异常
	 */
	protected PageSupport getPageSupport() throws PageSupportNotFoundException {
		ActionContext context = ActionContext.getContext();
		Object currentAction = context.getActionInvocation().getAction();
		if (currentAction instanceof PageSupport) {
			return (PageSupport) currentAction;
		}
		// #"PageSupprot not found. This Action must be extends from PageSupport."
		throw new PageSupportNotFoundException();
	}
}
